package service

import (
	"context"
	"database/sql"

	dbmodel "gitee.com/hp-huiw/family-expense-api/internal/db/model"
	"gitee.com/hp-huiw/family-expense-api/internal/dto"
	"gitee.com/hp-huiw/family-expense-api/internal/repository"
	"gitee.com/hp-huiw/my-go-core/date"
	"gitee.com/hp-huiw/my-go-core/security"
	"gitee.com/hp-huiw/my-go-core/util"
	"github.com/samber/lo"
)

type RoleService interface {
	Initialize(ctx context.Context) error
	QueryID(ctx context.Context, authority string) (string, error)
	QueryNonSystemLevelRoles(ctx context.Context) ([]dto.RoleResponse, error)
	Query(ctx context.Context) ([]dto.RoleResponse, error)
	Count(ctx context.Context, ids []string, authority string) (int64, error)
}

func NewRoleService(db *sql.DB) RoleService {
	return &roleService{
		db:             db,
		roleRepository: repository.NewRoleRepository(db),
	}
}

func (s *roleService) Initialize(ctx context.Context) error {
	exists, err := s.roleRepository.Exists(ctx)

	if err != nil {
		return err
	}

	if !exists {
		models := []dbmodel.Role{
			{
				ID:          util.GenerateUUIDWithoutDashes(),
				Authority:   security.RoleSystemAdmin,
				Title:       "系统管理员",
				Description: util.AnyPtr("当前整个系统的管理员，不属于任何账户。"),
				DateCreated: date.GetCurrentDate(),
			},
			{
				ID:          util.GenerateUUIDWithoutDashes(),
				Authority:   security.RoleAccountGroupAdmin,
				Title:       "账户管理员",
				Description: util.AnyPtr("可对当前账户组下的各项，如银行卡片和收支类别等进行管理。"),
				DateCreated: date.GetCurrentDate(),
			},
			{
				ID:          util.GenerateUUIDWithoutDashes(),
				Authority:   security.RoleAccountGroupUser,
				Title:       "用户",
				Description: util.AnyPtr("账户组普通用户"),
				DateCreated: date.GetCurrentDate(),
			},
			{
				ID:          util.GenerateUUIDWithoutDashes(),
				Authority:   security.RoleAccountGroupViewer,
				Title:       "查看者",
				Description: util.AnyPtr("账户组只读用户，仅可查看，不可操作数据。"),
				DateCreated: date.GetCurrentDate(),
			},
		}

		err = s.roleRepository.BatchInsert(ctx, models)
	}

	return err
}

// QueryID queries the role id by authority.
// Authority. AKA role name.
func (s *roleService) QueryID(ctx context.Context, authority string) (string, error) {
	return s.roleRepository.QueryID(ctx, authority)
}

// QueryNonSystemLevelRoles queries non system level roles with id and title.
func (s *roleService) QueryNonSystemLevelRoles(ctx context.Context) ([]dto.RoleResponse, error) {
	// All system level roles which will be ignored from this result.
	systemRoles := []string{
		security.RoleSystemAdmin,
	}

	query, err := s.roleRepository.QueryNotIn(ctx, systemRoles)

	if err != nil {
		return nil, err
	}

	result := lo.Map(query, func(item repository.Role, _ int) dto.RoleResponse {
		return dto.RoleResponse{
			ID:          item.ID,
			Title:       item.Title,
			Authority:   item.Authority,
			Description: item.Description,
		}
	})

	return result, nil
}

// Query queries all roles with id and title.
func (s *roleService) Query(ctx context.Context) ([]dto.RoleResponse, error) {
	query, err := s.roleRepository.Query(ctx)

	if err != nil {
		return nil, err
	}

	result := lo.Map(query, func(item repository.Role, _ int) dto.RoleResponse {
		return dto.RoleResponse{
			ID:    item.ID,
			Title: item.Title,
		}
	})

	return result, nil
}

func (s *roleService) Count(ctx context.Context, ids []string, authority string) (int64, error) {
	return s.roleRepository.Count(ctx, ids, authority)
}

// Container of RoleService functions.
type roleService struct {
	db             *sql.DB
	roleRepository repository.RoleRepository
}
